Skip to content

Fast most_recent_only belief lookup for sensor statuses - #2463

Open
Ahmad-Wahid wants to merge 2 commits into
mainfrom
fast-most-recent-belief-lookup
Open

Fast most_recent_only belief lookup for sensor statuses#2463
Ahmad-Wahid wants to merge 2 commits into
mainfrom
fast-most-recent-belief-lookup

Conversation

@Ahmad-Wahid

Copy link
Copy Markdown
Contributor

Closes #2445.

The problem

The asset page shows, for each sensor, how up to date its data is per source type. To work that out, it ran one "most recent belief" query per source type — all seven of them — for every sensor on the page.

Each query asks for the newest belief whose source is of a given type. Nothing on the beliefs table can index that, so PostgreSQL walks the sensor's events from newest to oldest, checking each belief's source type until it finds a match. When a type recorded nothing for that sensor — which is the normal case, since most sensors have data from only one or two types — there is no match to find, so it reads every belief the sensor has before returning nothing.

So the page paid a full scan of a sensor's data five or six times per sensor, and it got slower as the data grew.

The fix

We now know which sources ever recorded for a sensor from the small sensor_data_source summary table (#2382) — a few rows, not a scan. So:

  • look up the sensor's sources once, and group them by type;
  • skip a type with no sources entirely — no query at all, which is where the scans were;
  • for the types that remain, name the sources in the query instead of filtering on their type, which lets the reordered primary key (Pin and reorder the timed_belief primary key #2378) find the newest belief by reading a single row.

Result

Timing the whole per-sensor status lookup, on a sensor with beliefs from one source:

beliefs on the sensor before after
100,000 86 ms 5 ms
300,000 214 ms 4 ms
1,000,000 655 ms 5 ms

The old version got slower as the sensor grew. The new one does not. An asset page multiplies these by its number of sensors.

Does it still give the same answers?

Yes.

The summary table is a superset: it can list a source whose beliefs have since been deleted, but it can never miss a source that has data. A stale entry only costs one query that comes back empty — exactly what happened before — so no status can change because of it.

The source and exclude_source_types filters of a sensor's staleness search are now applied when looking up the sources rather than when querying the beliefs, so they still apply.

Tests

Four new tests in flexmeasures/data/tests/test_sensor_status_queries.py check that only the source types that actually recorded get queried, that a newly added type is picked up, and that both source filters are honoured. Three of them fail against the old code (7 queries where 1 or 2 are expected); the fourth guards the filter behaviour.

Verified: flexmeasures/ui (115 passed), the sensor/asset API tests (144 passed), and the relevant data tests (75 passed). Two unrelated pre-existing segfaults on my machine (the MILP solver in test_commitments.py, and the fakeredis job tests) stop the full flexmeasures/data run; both crash the same way on an unmodified main.

🤖 Generated with Claude Code

…g its beliefs

The asset page reports, per sensor, how up to date each source type's data is.
It got that by running one most_recent_only search per source type,
for all seven default types, for every sensor on the page.

Each of those is timely-beliefs' fast track,
ORDER BY event_start DESC, belief_horizon ASC LIMIT 1,
but with a join to data_source filtering on its type.
That filter is the part no index on timed_belief can serve:
PostgreSQL walks the sensor's events from the newest backwards,
rechecking the type of each belief's source until it finds a match.
Where a type recorded nothing for that sensor -- the common case, since most
sensors have data from one or two types -- there is no match to find,
so the scan reads every belief the sensor has before returning empty.
The cost is therefore paid several times per sensor and grows with the data.

The sensor_data_source summary (#2382) now answers which sources ever recorded
for a sensor as a handful of rows. So resolve the sensor's sources once,
group them by type, and name them in the belief query instead of filtering on
their type. A type with no sources is skipped without a query at all, which is
where the scans were, and a named source lets the reordered primary key (#2378),
which leads with (sensor_id, source_id, event_start, belief_horizon),
answer the LIMIT 1 as a backwards index scan reading a single row.

Measured against a sensor carrying beliefs from one source, timing the whole
per-sensor status lookup: at 300k beliefs 210 ms -> 5 ms, at 1M beliefs
611 ms -> 5 ms. The old path grows with the row count; the new one does not.

Semantics are unchanged. The summary is a documented superset, so it can list a
source whose beliefs have since been deleted -- costing one query that returns
nothing, exactly as before -- but it cannot omit a source that has data.
The source and exclude_source_types filters of the staleness search are applied
to the source lookup instead of to the belief query, so they still hold.

Tests pin that only the types that recorded are queried, that a second type is
picked up, and that both source filters are honoured. The first, second and
fourth fail against the old code with 7 queries where 1 or 2 are expected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Ahmad-Wahid
Ahmad-Wahid requested a lite review from Copilot September 2, 2026 15:44
@Ahmad-Wahid Ahmad-Wahid self-assigned this Sep 2, 2026
@Ahmad-Wahid Ahmad-Wahid added this to the 1.1.0 milestone Sep 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

New/updated docstrings violate the repo’s line-break-after-punctuation convention, which needs to be fixed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves the performance of per-sensor “most recent belief” lookups used to compute sensor status (staleness) on the asset page by skipping source types that never recorded for a sensor and querying beliefs by explicit sources (leveraging the sensor_data_source summary table and the reordered PK).

Changes:

  • Add _sensor_sources_by_type to resolve and group a sensor’s recorded sources (respecting source and exclude_source_types filters) before querying beliefs.
  • Update _get_sensor_bdfs_by_source_type to skip source types with no sources and pass source=[...] rather than source_types=[...] into TimedBelief.search.
  • Add targeted tests asserting the reduced query count and correct filter behavior, plus a changelog entry.
File summaries
File Description
flexmeasures/data/services/sensors.py Resolve sources once via summary table and query most-recent beliefs by explicit sources, skipping empty types
flexmeasures/data/tests/test_sensor_status_queries.py New tests validating query reduction and correctness of source / exclude_source_types filtering
documentation/changelog.rst Changelog entry documenting the asset-page performance improvement
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread flexmeasures/data/services/sensors.py Outdated
Comment thread flexmeasures/data/tests/test_sensor_status_queries.py Outdated
@read-the-docs-community

read-the-docs-community Bot commented Sep 2, 2026

Copy link
Copy Markdown

Documentation build overview

📚 flexmeasures | 🛠️ Build #34361744 | 📁 Comparing 23ebed1 against latest (8944a8b)

  🔍 Preview build  

2 files changed
± changelog.html
± api/v3_0.html

@nhoening

nhoening commented Sep 2, 2026

Copy link
Copy Markdown
Member

Nice, this comes in timely after #2382, getting a big benefit of that right away ❤️

Signed-off-by: Ahmad-Wahid <ahmedwahid16101@gmail.com>
@Ahmad-Wahid
Ahmad-Wahid requested a review from nhoening September 2, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fast most_recent_only belief lookup

3 participants